package com.joinway.framework.support.dao;

import java.lang.reflect.Method;
import java.util.Date;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.joinway.framework.core.utils.AopHelper;

public class DaoAspect {

	private final static Logger log = LoggerFactory.getLogger(DaoAspect.class);
	
	private String createByFieldName = "createBy";
	
	private String createDateFieldName = "createDate";
	
	private String updateByFieldName = "updateBy";
	
	private String updateDateFieldName = "updateDate";
	
	public Object doAround(ProceedingJoinPoint point) throws Throwable {
		Object[] args = point.getArgs();
		Object[] params = args;
		
		if(!ArrayUtils.isEmpty(args)){
			params = new Object[args.length];
			for(int i = 0; i < args.length; i++){
				if(args[i] instanceof String){
					params[i] = args[i] == null ? null : StringUtils.trimToNull(args[i].toString());
				}else{
					params[i] = args[i];
				}
			}
			
			Method method = AopHelper.getMethod(point);
			
			if(params.length == 1){
				Object param = params[0];
				if(param != null && !(param instanceof String) && !(param instanceof Date) && !(param instanceof Number)){
					resetFieldValues(param, method);
				}
			}
		}
		
		return point.proceed(params);
	}

	protected void resetFieldValues(Object domain, Method method){
		if(domain == null) return;
		
		Date today = new Date();
		String user = getUser();
		
		if(isInsertMethod(method)){
			try{
				if(PropertyUtils.isReadable(domain, createByFieldName) && PropertyUtils.isWriteable(domain, createByFieldName)){
					if(StringUtils.isNotBlank(user) && PropertyUtils.getProperty(domain, createByFieldName) == null){
						PropertyUtils.setProperty(domain, createByFieldName, user);
					}
				}
			}catch(Exception e){
				log.error("failed to set create by field");
			}
			
			try{
				if(PropertyUtils.isReadable(domain, createDateFieldName) && PropertyUtils.isWriteable(domain, createDateFieldName)){
					if(PropertyUtils.getProperty(domain, createDateFieldName) == null){
						PropertyUtils.setProperty(domain, createDateFieldName, today);
					}
				}
			}catch(Exception e){
				log.error("failed to set create date field");
			}
		}
		
		if(isUpdateMethod(method)){
			try{
				if(PropertyUtils.isReadable(domain, updateByFieldName) && PropertyUtils.isWriteable(domain, updateByFieldName)){
					if(StringUtils.isNotBlank(user) && PropertyUtils.getProperty(domain, updateByFieldName) == null){
						PropertyUtils.setProperty(domain, updateByFieldName, user);
					}
				}
			}catch(Exception e){
				log.error("failed to set update by field");
			}
			
			try{
				if(PropertyUtils.isReadable(domain, updateDateFieldName) && PropertyUtils.isWriteable(domain, updateDateFieldName)){
					if(PropertyUtils.getProperty(domain, updateDateFieldName) == null){
						PropertyUtils.setProperty(domain, updateDateFieldName, today);
					}
				}
			}catch(Exception e){
				log.error("failed to set update date field");
			}
		}
	}
	
	protected String getUser(){
		return null;
	}
	
	protected boolean isInsertMethod(Method method){
		return method.getName().toLowerCase().startsWith("insert");
	}
	
	protected boolean isUpdateMethod(Method method){
		return method.getName().toLowerCase().startsWith("update");
	}
	
	public String getCreateByFieldName() {
		return createByFieldName;
	}

	public void setCreateByFieldName(String createByFieldName) {
		this.createByFieldName = createByFieldName;
	}

	public String getCreateDateFieldName() {
		return createDateFieldName;
	}

	public void setCreateDateFieldName(String createDateFieldName) {
		this.createDateFieldName = createDateFieldName;
	}

	public String getUpdateByFieldName() {
		return updateByFieldName;
	}

	public void setUpdateByFieldName(String updateByFieldName) {
		this.updateByFieldName = updateByFieldName;
	}

	public String getUpdateDateFieldName() {
		return updateDateFieldName;
	}

	public void setUpdateDateFieldName(String updateDateFieldName) {
		this.updateDateFieldName = updateDateFieldName;
	}
	
}

